﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Up2Link.Core;

namespace Up2Link.Server
{
    /// <summary>
    /// 服务器类
    /// </summary>
    /// <typeparam name="T">消息格式</typeparam>
    public class CServer<T>
        where T : IMessage, new()
    {
        /// <summary>
        /// 用于监听并接受客户端的连接请求
        /// </summary>
        private TcpListener server = null;

        /// <summary>
        /// 客户端连接列表
        /// </summary>
        private List<CTcpConnection> connections = new List<CTcpConnection>();

        /// <summary>
        /// 客户端列表中的空位，用于减少创建销毁客户端连接对象的频度
        /// </summary>
        private Stack<Int32> emptyConnections = new Stack<Int32>();

        /// <summary>
        /// 存放接收到的消息的队列
        /// </summary>
        private Queue<CMessagePack<T>> recvMsg = new Queue<CMessagePack<T>>();

        /// <summary>
        /// 存放等待发送的消息的队列
        /// </summary>
        private Queue<CMessagePack<T>> sendMsg = new Queue<CMessagePack<T>>();

        /// <summary>
        /// 存放服务器消息的队列
        /// </summary>
        private Queue<CServerLog> logMsg = new Queue<CServerLog>();

        /// <summary>
        /// 标记服务器状态
        /// </summary>
        private Boolean serverOn = false;

        /// <summary>
        /// 服务器的逻辑放在主线程以外执行，使用户可以利用主线程执行自己的代码
        /// </summary>
        private Thread update = null;

        /// <summary>
        /// 构造函数
        /// 创建update线程
        /// </summary>
        public CServer()
        {
            update = new Thread(new ThreadStart(Update));
        }

        /// <summary>
        /// 启动服务器 
        /// </summary>
        /// <param name="address">服务器ip地址</param>
        /// <param name="port">服务器端口号</param>
        /// <returns>如果启动过程中发生异常或错误，函数返回false，否则返回true</returns>
        /// <example>server.Start("127.0.0.1", 1234);</example>
        public Boolean Start(String address, Int32 port)
        {
            try
            {
                //创建监听器
                server = new TcpListener(IPAddress.Parse(address), port);

                //启动监听器
                server.Start();
            }
            catch (Exception e)
            {
                //捕捉到异常时，停止监听器
                if (server != null)
                {
                    server.Stop();
                }
                //System.Console.WriteLine(e.StackTrace);

                //返回false
                return false;
            }

            //监听器正常启动，将服务器标记为运行
            serverOn = true;

            //启动服务器逻辑线程
            update.Start();

            //返回true
            return true;
        }

        /// <summary>
        /// 返回服务器状态标记
        /// </summary>
        /// <returns>服务器状态标记</returns>
        public Boolean isServerOn()
        {
            return serverOn;
        }

        /// <summary>
        /// 关闭指定的客户端连接
        /// </summary>
        /// <param name="connectionIndex">客户端连接的索引号</param>
        public void CloseConnection(Int32 connectionIndex)
        {
            //重置各项客户端状态
            connections[connectionIndex].Status = 0;
            //connections[connectionIndex].ID = "";

            //调用CTcpConnection对象的Close函数，释放资源
            connections[connectionIndex].Close();

            //将闲置的客户端连接序号入栈
            emptyConnections.Push(connectionIndex);

            //记录服务器消息
            //CServerLog log = new CServerLog();
            //log.logCode = CServerLog.CLIENT_DISCONNECTED;
            //log.logMessage = connectionIndex;
            //logMsg.Enqueue(log);
        }

        /// <summary>
        /// 关闭服务器
        /// </summary>
        public void Close()
        {
            //标记服务器状态为停止
            serverOn = false;                        

            //等待update线程结束
            update.Join();

            //关闭所有客户端连接
            foreach (CTcpConnection tc in connections)
            {
                tc.Close();
            }

            //清空列表和栈
            connections.Clear();
            emptyConnections.Clear();

            //终止监听器
            server.Stop();
        }

        /// <summary>
        /// 返回服务器接收到的消息
        /// </summary>
        /// <returns>返回队列中的第一条消息，如果队列为空则返回null</returns>
        public CMessagePack<T> Receive()
        {
            if (recvMsg.Count > 0)
            {
                return recvMsg.Dequeue();
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 将等待发送的消息交给服务器
        /// </summary>
        /// <param name="msgPack">一条消息</param>
        public void Send(CMessagePack<T> msgPack)
        {
            sendMsg.Enqueue(msgPack);
        }

        /// <summary>
        /// 返回服务器产生的日志信息
        /// </summary>
        /// <returns>返回队列中的第一条日志信息，如果队列为空则返回null</returns>
        public CServerLog GetServerLog()
        {
            if (logMsg.Count > 0)
            {
                return logMsg.Dequeue();
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 服务器逻辑
        /// </summary>
        private void Update()
        {
            //循环条件为服务器正在运行
            while (serverOn)
            {
                try
                {
                    //如果有客户端等待与服务器建立连接，则创建与该客户端的连接
                    if (server.Pending())
                    {
                        TcpClient tempTC = server.AcceptTcpClient();
                        CTcpConnection tempP = new CTcpConnection(tempTC);
                        tempP.Status = 10;

                        Int32 connectionIndex;

                        if (emptyConnections.Count > 0)
                        {
                            Int32 count = emptyConnections.Pop();
                            connections[count] = tempP;

                            connectionIndex = count;
                        }
                        else
                        {
                            connections.Add(tempP);

                            connectionIndex = connections.Count - 1;
                        }

                        //记录服务器消息
                        CServerLog log = new CServerLog();
                        log.logCode = CServerLog.NEW_CLIENT_CONNECTION;
                        log.logMessage = connectionIndex;
                        logMsg.Enqueue(log);
                    }


                    //遍历客户端连接，检查连接是否断开，并接收来自客户端的消息
                    for (Int32 i = 0; i < connections.Count; i++)
                    {
                        if (connections[i].Status > 0)
                        {
                            //判断客户端是否断开
                            if (!connections[i].IsClientOn())
                            {
                                connections[i].Status = 0;
                                connections[i].Close();
                                emptyConnections.Push(i);

                                CServerLog log = new CServerLog();
                                log.logCode = CServerLog.CLIENT_DISCONNECTED;
                                log.logMessage = i;
                                logMsg.Enqueue(log);
                            }
                            else
                            {
                                //更新客户端连接
                                connections[i].Update();

                                //接收消息
                                T tempT = new T();
                                Byte[] rcvData = connections[i].Receive();
                                if (rcvData != null)
                                {
                                    tempT.Decode(rcvData);
                                    recvMsg.Enqueue(new CMessagePack<T>(tempT, i));
                                }
                            }
                        }
                    }

                    //遍历待发送消息队列，发送其中的全部消息
                    while (sendMsg.Count > 0)
                    {
                        CMessagePack<T> tempMP = sendMsg.Dequeue();
                        connections[tempMP.index].Send(tempMP.message.Encode());
                    }
                }
                catch (Exception e)
                {
                    serverOn = false;

                    CServerLog log = new CServerLog();
                    log.logCode = CServerLog.SERVER_EXCEPTION;
                    log.logMessage = 0;
                    logMsg.Enqueue(log);

                    //System.Console.WriteLine(e.StackTrace);
                }

                Thread.Sleep(100 * (connections.Count + 1));
            }
        }
    }
}
